/*
 * The MIT License (MIT)
 * 
 * Copyright (c) 2014 Dameng Database Inc.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package com.dm.demo.repository.mybatis.config;

import com.dm.demo.repository.mybatis.po.IdeaPo;
import com.dm.demo.repository.pojo.IdeaPojo;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.jdbc.ScriptRunner;
import org.apache.ibatis.session.SqlSessionFactory;
import org.dozer.DozerBeanMapper;
import org.dozer.Mapper;
import org.dozer.loader.api.BeanMappingBuilder;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.Reader;
import java.sql.SQLException;

/**
 * Title.
 * <p/>
 * Description.
 *
 * @author Bill Lv {@literal <billcc.lv@hotmail.com>}
 * @version 1.0
 * @since 2014-12-05
 */
@Configuration
@ComponentScan(basePackages = {"com.dm.demo.repository.mybatis"})
@MapperScan("com.dm.demo.repository.mybatis.mapper")
@EnableTransactionManagement
@PropertySources({
        @PropertySource("classpath:/init.properties")
})
public class MybatisConfig {
    private static final Logger LOGGER = LoggerFactory.getLogger(MybatisConfig.class);

    private static final String CHECK_TABLE_EXISTENCE_SQL = "SELECT COUNT(A.TABLE_NAME)\n" +
            "\tFROM USER_TABLES UT\n" +
            "\tJOIN ALL_TABLES A\n" +
            "\tON (\n" +
            "\t\tUT.TABLESPACE_NAME = A.TABLESPACE_NAME\n" +
            "\t\tAND UT.TABLE_NAME = A.TABLE_NAME\n" +
            "\t )\n" +
            "\tWHERE\n" +
            "\tA.OWNER = ?\n" +
            "\tAND A.TABLE_NAME = ?";

    @Autowired
    private DataSource dataSource;

    @Autowired
    private Environment env;

    @Bean
    public DataSourceTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        initDB();
        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource);
        sessionFactoryBean.setTypeAliasesPackage("com.dm.demo.repository.mybatis.po");
        return sessionFactoryBean.getObject();
    }

    private void initDB() throws SQLException, IOException {
        String owner = null;
        if (dataSource instanceof ComboPooledDataSource) {
            owner = ((ComboPooledDataSource) dataSource).getUser();
        }
        if (owner == null) {
            throw new UnsupportedOperationException("The data source you have registered is not c3p0, " +
                    "please try to add your code above to solve the problem.");
        }
        String[] tables = env.getRequiredProperty("init.tables", String[].class);
        boolean needsInitTable = false;
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        for (String table : tables) {
            Integer tableCount = jdbcTemplate.queryForObject(CHECK_TABLE_EXISTENCE_SQL, Integer.class, owner, table);
            if (tableCount == 0) {
                needsInitTable = true;
                break;
            }
        }
        if (needsInitTable) {
            runSqlScript("sqlscripts/table_init.sql");
        }
        runSqlScript("sqlscripts/function_init.sql");
    }

    private void runSqlScript(String script) throws SQLException, IOException {
        ScriptRunner scriptRunner = new ScriptRunner(dataSource.getConnection());
        scriptRunner.setDelimiter("#DELIMITER#");
        scriptRunner.setStopOnError(true);
        Reader reader = Resources.getResourceAsReader(script);
        try {
            scriptRunner.runScript(reader);
        } catch (Exception e) {
            LOGGER.error(String.format("%s execution failed", script), e);
            throw new RuntimeException("init script load error", e);
        } finally {
            if (scriptRunner != null) {
                scriptRunner.closeConnection();
            }
        }
    }

    @Bean
    public BeanMappingBuilder beanMappingBuilder() {
        BeanMappingBuilder builder = new BeanMappingBuilder() {
            protected void configure() {
                mapping(IdeaPo.class, IdeaPojo.class);
            }
        };
        return builder;
    }

    @Bean
    public Mapper mapper() {
        DozerBeanMapper mapper = new DozerBeanMapper();
        mapper.addMapping(beanMappingBuilder());
        return mapper;
    }
}
